package org.msgcenter.mc.actor;

import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.CompositeFuture;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpServer;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import lombok.extern.slf4j.Slf4j;
import org.msgcenter.mc.common.ConvertUtil;
import org.msgcenter.mc.common.JsonResult;
import org.msgcenter.mc.ds.*;

import java.util.function.BiConsumer;
import java.util.function.BiFunction;

@Slf4j
public class MsgHttpReqPipelineActor extends AbstractVerticle {

  private EventBus eb;

  @Override
  public void start(Promise<Void> startPromise) throws Exception {
    super.start(startPromise);

    Router router = Router.router(vertx);
    eb = vertx.eventBus();

    HttpServer server = vertx.createHttpServer();
    server.requestHandler(router)
      .listen(8880)
      .onSuccess(http -> log.info("消息发送http服务启动成功 监听8880端口"))
      .onFailure(err -> log.error("消息发送http服务启动失败 {}", err.getMessage()));

    onSendSiteMsg(router);
    onCompanyWechatMsg(router);
  }



  @SuppressWarnings("DuplicatedCode")
  void onCompanyWechatMsg(Router router) {
    Route route = router.post("/send/cwMsg");

    // 检查消息模板 和 消息内容是否符合模板
    BiFunction<RoutingContext, ReqCompanyWechatMsg, Future<TplRequestMsg>> checkTpl = (rc, cwm) -> {
      TplRequestMsg tplRequestMsg = new TplRequestMsg(cwm.getTemplateId(), "", cwm.getMsgContent());
      return eb.request("service.tpl.check_by_id", JSONUtil.toJsonStr(tplRequestMsg))
        .compose(payload -> {
          JsonResult<String, String> jr = JSONUtil.toBean(payload.body().toString(), new TypeReference<>() {
          }, true);

          if (!jr.isOk()) {
            String errLog = StrUtil.format("{} 模板id: {} 模板内容: {}", "模板id和内容不匹配", cwm.getTemplateId(), cwm.getMsgContent());
            rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(errLog));
            return Future.failedFuture(errLog);
          }

          return Future.succeededFuture(tplRequestMsg);
        })
        .onFailure(err -> log.error("检查模板出现错误 {}", err.getMessage()));
    };

    // 检查请求体基础属性
    BiConsumer<RoutingContext, ReqCompanyWechatMsg> checkRequestBase = (rc, cwm) -> {
      if (!ArrayUtil.contains(SendMsgHelpers.msgFromRange, cwm.getMsgFrom())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_MSG_FROM_RANGE, cwm.getMsgFrom())));
        return;
      }

      if (!ArrayUtil.contains(SendMsgHelpers.msgTypeRange, cwm.getMsgType())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_MSG_TYPE_RANGE, cwm.getMsgType())));
      }

      if (!ArrayUtil.contains(SendMsgHelpers.msgPriorityRange, cwm.getMsgPriority())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_MSG_PRIORITY_RANGE, cwm.getMsgPriority())));
        return;
      }

      if (!ArrayUtil.contains(SendMsgHelpers.pushTypeRange, cwm.getPushType())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_PUSH_TYPE_RANGE, cwm.getPushType())));
        return;
      }

      if (!ArrayUtil.contains(SendMsgHelpers.tipTypeRange, cwm.getTipType())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_TIP_TYPE_RANGE, cwm.getTipType())));
        return;
      }

      // 定时推送判断
      boolean pushTimeCheckFlag = System.currentTimeMillis() < (cwm.getPushTime().longValue() * 1000 - 5000);
      String prettyLog = StrUtil.format("{} < {}", System.currentTimeMillis(), cwm.getPushTime().longValue() * 1000 - 5000);
      if (cwm.getPushType() == 1 && !pushTimeCheckFlag) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "定时推送时间过于紧凑", prettyLog)));
        return;
      }

      if (StrUtil.isBlank(cwm.getReceiveManId())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息接受人id为空", cwm.getReceiveManId())));
        return;
      }

      if (cwm.getTemplateId() <= 0) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息模板id为空", cwm.getTemplateId())));
        return;
      }

      if (StrUtil.isBlank(cwm.getMsgContent())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息内容为空", cwm.getMsgContent())));
        return;
      }

      if (!JSONUtil.isTypeJSON(cwm.getMsgContent())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息内容格式不正确", cwm.getMsgContent())));
      }

      log.info("完成站点消息基础属性请求");
    };

    BiFunction<RoutingContext, CompanyWechatMsg, Future<Object>> checkMsgId = (rc, cwm) -> eb
      .request("site_msg.msg_id.check", JSONUtil.toJsonStr(cwm))
      .compose(payload -> {
        JsonResult<String, String> jr = JSONUtil.toBean(payload.body().toString(), new TypeReference<>() {
        }, true);

        if (!jr.isOk()) {
          String errLog = StrUtil.format("站点消息id出现重复: {}", cwm.getMsgId());
          rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(errLog));
          return Future.failedFuture(errLog);
        }

        log.info("完成站点消息id重复性检查");
        return Future.succeededFuture();
      })
      .onFailure(err -> log.error("检查消息id出现错误 {}", err.getMessage()));

    route
      .handler(BodyHandler.create())
      .handler(rc -> {
        ReqCompanyWechatMsg cwm = JSONUtil.toBean(rc.body().asString(), new TypeReference<>() {
        }, true);

        checkRequestBase.accept(rc, cwm);

        // 转换为统一消息请求数据结构
        RequestMsg rm = ConvertUtil.companyWechatMsgToRequestMsg(cwm);
        // 设置发送渠道
        rm.setChannelType(SendMsgHelpers.BASE_COMPANY_WECHAT_MSG);

        // 检查模板
        Future<TplRequestMsg> checkTplResult = checkTpl.apply(rc, cwm);

        // 检查msg_id是否重复
        CompanyWechatMsg wm = new CompanyWechatMsg();
        wm.setMsgId(rm.getMsgId());
        Future<Object> checkMsgIdResult = checkMsgId.apply(rc, wm);

        // 两个条件必须检查通过
        CompositeFuture.all(checkTplResult, checkMsgIdResult)
          .onSuccess(ar -> {

            // 渲染模板
            TplRequestMsg trm = new TplRequestMsg();
            trm.setId(cwm.getTemplateId());
            trm.setTplContent(cwm.getMsgContent());

            eb.request("service.tpl.render", JSONUtil.toJsonStr(trm))
              .onSuccess(paylodadata -> {
                JsonResult<String, String> jr = JSONUtil.toBean(paylodadata.body().toString(), new TypeReference<>() {
                }, true);

                if (jr.isOk()) {
                  log.info("完成站点消息模板渲染");
                  rm.setMsgContent(jr.getData());
                  eb.send("service.dispatch", JSONUtil.toJsonStr(rm));

                  JsonResult<String, String> returnJr = new JsonResult<>(HttpStatus.HTTP_OK, "消息已经进入发送队列", rm.getMsgId().toString());
                  rc.end(JSONUtil.toJsonStr(returnJr));
                  return;
                }

                log.error("渲染模板失败 {}", jr.getMsg());
                rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("渲染模板失败 {}", jr.getMsg())));
              });
          });
      })
      .failureHandler(ctx -> {
        JsonResult<String, String> jr = new JsonResult<>(ctx.statusCode(), ctx.failure().getMessage(), "");
        ctx.response()
          .putHeader("content-type", "application/json;charset=utf-8")
          .end(JSONUtil.toJsonStr(jr));
      });
  }


  // 接受站点消息发送
  void onSendSiteMsg(Router router) {
    Route route = router.post("/send/siteMsg");

    // 检查消息模板 和 消息内容是否符合模板
    BiFunction<RoutingContext, ReqSiteMsg, Future<TplRequestMsg>> checkTpl = (rc, rsm) -> {
      TplRequestMsg tplRequestMsg = new TplRequestMsg(rsm.getTemplateId(), "", rsm.getMsgContent());
      return eb.request("service.tpl.check_by_id", JSONUtil.toJsonStr(tplRequestMsg))
        .compose(payload -> {
          JsonResult<String, String> jr = JSONUtil.toBean(payload.body().toString(), new TypeReference<>() {
          }, true);

          if (!jr.isOk()) {
            String errLog = StrUtil.format("{} 模板id: {} 模板内容: {}", "模板id和内容不匹配", rsm.getTemplateId(), rsm.getMsgContent());
            rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(errLog));
            return Future.failedFuture(errLog);
          }

          return Future.succeededFuture(tplRequestMsg);
        })
        .onFailure(err -> log.error("检查模板出现错误 {}", err.getMessage()));
    };

    // 检查消息id
    BiFunction<RoutingContext, SiteMsg, Future<Object>> checkMsgId = (rc, sm) -> eb
      .request("site_msg.msg_id.check", JSONUtil.toJsonStr(sm))
      .compose(payload -> {
        JsonResult<String, String> jr = JSONUtil.toBean(payload.body().toString(), new TypeReference<>() {
        }, true);

        if (!jr.isOk()) {
          String errLog = StrUtil.format("站点消息id出现重复: {}", sm.getMsgId());
          rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(errLog));
          return Future.failedFuture(errLog);
        }

        log.info("完成站点消息id重复性检查");
        return Future.succeededFuture();
      })
      .onFailure(err -> log.error("检查消息id出现错误 {}", err.getMessage()));

    // 检查请求体基础属性
    BiConsumer<RoutingContext, ReqSiteMsg> checkRequestBase = (rc, rsm) -> {
      if (!ArrayUtil.contains(SendMsgHelpers.msgFromRange, rsm.getMsgFrom())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_MSG_FROM_RANGE, rsm.getMsgFrom())));
        return;
      }

      if (!ArrayUtil.contains(SendMsgHelpers.msgTypeRange, rsm.getMsgType())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_MSG_TYPE_RANGE, rsm.getMsgType())));
      }

      if (!ArrayUtil.contains(SendMsgHelpers.msgPriorityRange, rsm.getMsgPriority())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_MSG_PRIORITY_RANGE, rsm.getMsgPriority())));
        return;
      }

      if (!ArrayUtil.contains(SendMsgHelpers.pushTypeRange, rsm.getPushType())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_PUSH_TYPE_RANGE, rsm.getPushType())));
        return;
      }

      if (!ArrayUtil.contains(SendMsgHelpers.tipTypeRange, rsm.getTipType())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", SendMsgHelpers.ERROR_TIP_TYPE_RANGE, rsm.getTipType())));
        return;
      }

      // 定时推送判断
      boolean pushTimeCheckFlag = System.currentTimeMillis() < (rsm.getPushTime().longValue() * 1000 - 5000);
      String prettyLog = StrUtil.format("{} < {}", System.currentTimeMillis(), rsm.getPushTime().longValue() * 1000 - 5000);
      if (rsm.getPushType() == 1 && !pushTimeCheckFlag) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "定时推送时间过于紧凑", prettyLog)));
        return;
      }

      if (StrUtil.isBlank(rsm.getReceiveManId())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息接受人id为空", rsm.getReceiveManId())));
        return;
      }

      if (rsm.getTemplateId() <= 0) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息模板id为空", rsm.getTemplateId())));
        return;
      }

      if (StrUtil.isBlank(rsm.getMsgContent())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息内容为空", rsm.getMsgContent())));
        return;
      }

      if (!JSONUtil.isTypeJSON(rsm.getMsgContent())) {
        rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("{} {}", "消息内容格式不正确", rsm.getMsgContent())));
      }

      log.info("完成站点消息基础属性请求");
    };

    route
      .handler(BodyHandler.create())
      .handler(rc -> {
        ReqSiteMsg rsm = JSONUtil.toBean(rc.body().asString(), new TypeReference<>() {
        }, true);

        checkRequestBase.accept(rc, rsm);

        // 转换为统一消息请求
        RequestMsg rm = ConvertUtil.reqSiteMsgToRequestMsg(rsm);
        // 设置发送渠道
        rm.setChannelType(SendMsgHelpers.BASE_SITE_MSG);

        // 检查模板
        Future<TplRequestMsg> checkTplResult = checkTpl.apply(rc, rsm);

        // 检查msg_id是否重复
        SiteMsg siteMsg = new SiteMsg();
        siteMsg.setMsgId(rm.getMsgId());
        Future<Object> checkMsgIdResult = checkMsgId.apply(rc, siteMsg);

        // 两个条件必须检查通过
        CompositeFuture.all(checkTplResult, checkMsgIdResult)
          .onSuccess(ar -> {

            // 渲染模板
            TplRequestMsg trm = new TplRequestMsg();
            trm.setId(rsm.getTemplateId());
            trm.setTplContent(rsm.getMsgContent());

            eb.request("service.tpl.render", JSONUtil.toJsonStr(trm))
              .onSuccess(paylodadata -> {
                JsonResult<String, String> jr = JSONUtil.toBean(paylodadata.body().toString(), new TypeReference<>() {
                }, true);

                if (jr.isOk()) {
                  log.info("完成站点消息模板渲染");
                  rm.setMsgContent(jr.getData());
                  eb.send("service.dispatch", JSONUtil.toJsonStr(rm));

                  JsonResult<String, String> returnJr = new JsonResult<>(HttpStatus.HTTP_OK, "消息已经进入发送队列", rm.getMsgId().toString());
                  rc.end(JSONUtil.toJsonStr(returnJr));
                  return;
                }

                log.error("渲染模板失败 {}", jr.getMsg());
                rc.fail(HttpStatus.HTTP_BAD_REQUEST, new Throwable(StrUtil.format("渲染模板失败 {}", jr.getMsg())));
              });
          });
      })
      .failureHandler(ctx -> {
        JsonResult<String, String> jr = new JsonResult<>(ctx.statusCode(), ctx.failure().getMessage(), "");
        ctx.response()
          .putHeader("content-type", "application/json;charset=utf-8")
          .end(JSONUtil.toJsonStr(jr));
      });
  }

  @Override
  public void stop(Promise<Void> stopPromise) throws Exception {
    super.stop(stopPromise);
  }
}
